﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

using mvcForum.Core;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Core.Specifications;

using mvcForum.Web.Controllers;
using mvcForum.Web.Extensions;
using mvcForum.Web.Helpers;
using mvcForum.Web.ViewModels;
using mvcForum.Web.Interfaces;
using System.Web.Routing;

namespace mvcForum.Web.Areas.Forum.Controllers {

	/// <summary>
	/// Forum controller.
	/// The controller handles showing a given forum and any topics and sub-forums belonging to that forum.
	/// </summary>
	public class ForumController : ThemedForumBaseController {
		private readonly IConfiguration config;
		private readonly ITopicRepository topicRepo;
		private readonly IForumRepository forumRepo;

		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="config">Injected IConfiguration instance.</param>
		/// <param name="userProvider">Injected IWebUserProvider instance.</param>
		/// <param name="context">Injected IContext instance.</param>
		/// <param name="topicRepo">Injected ITopicRepository instance.</param>
		/// <param name="forumRepo">Injected IForumRepository instance.</param>
		public ForumController(IConfiguration config, IWebUserProvider userProvider, IContext context, ITopicRepository topicRepo, IForumRepository forumRepo)
			: base(userProvider, context) {

			// Store the configuration instance as a variable on the controller instance.
			this.config = config;
			// Store the topic repository instance as a variable on the controller instance.
			this.topicRepo = topicRepo;
			this.forumRepo = forumRepo;
		}

		#region Read
		/// <summary>
		/// The regular index/read method for a forum.
		/// </summary>
		/// <param name="id">The Id of the forum that should be shown.</param>
		/// <param name="title">The title/slug of the forum, if the title/slug is wrong, a permanent redirect to the right Url is returned.</param>
		/// <param name="page">The page index.</param>
		/// <returns></returns>
		public ActionResult Index(Int32 id, String title, Int32? page) {
			// Get the forum.
			Core.Forum forum = this.forumRepo.ReadOneOptimized(new ForumSpecifications.ById(id));
			// Did we get a forum?
			if (forum == null) {
				// No, let's return "resource not found"!
				return new HttpStatusCodeResult(404);
			}

			// Is the title correct?
			if (title != forum.Name.ToSlug()) {
				// No, let's permanent redirect to the correct URL!
				return RedirectPermanent(
											Url.RouteUrl("ShowForum", new {
												area = "forum",
												title = forum.Name.ToSlug(),
												id = forum.Id,
												page = page.HasValue ? page.Value : 1
											})
										);
			}

			// Let's create the model.
			ForumViewModel model = new ForumViewModel(forum, config.TopicsPerPage, this.config.AuthorNameSetting);
			model.Paging.Page = (page.HasValue ? page.Value : 1);

			// Do the user have read access to the forum?
			if (forum.HasAccess(AccessFlag.Read)) {
				// The forum has been shown!
				forum.Track();

				// Is the user a moderator?
				Boolean isModerator = forum.HasAccess(AccessFlag.Moderator);
				// TODO: !?!?!??!
				isModerator = false;

				// Get the topics to show the user.
				IList<Topic> topics = this.topicRepo.ReadTopics(forum, model.Paging.Page, this.Authenticated ? this.ActiveUser : null, isModerator);

				// Create the view models for the topics!
				model.Topics = topics.Select(x => new TopicViewModel((x), new MessageViewModel[] { }, x.Posts.Visible(this.config).Count() - 1, config.MessagesPerPage, false, this.config.AllowQuickReply, this.config.AuthorNameSetting));

				// Get any sub-forums of the forum, we'll show those to the user too!
				model.SubForums = forum.SubForums.Select(x => new ForumViewModel(x, config.TopicsPerPage, this.config.AuthorNameSetting));

				model.Path = new Dictionary<String, String>();
				// Build the breadcrumb path.
				HomeController.BuildPath(forum, model.Path, this.Url);

				return View(model);
			}

			// The user is not allowed to read the forum, let's tell him!
			TempData.Add("Reason", ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("NoAccess.Forum", new { Name = forum.Name }));
			// Off to the "no access" page.
			return RedirectToRoute("NoAccess");
		}
		#endregion

		#region Misc
		/// <summary>
		/// The method marks all topics of the given forum as read.
		/// </summary>
		/// <param name="id">Id of the forum.</param>
		/// <returns></returns>
		[Authorize]
		[ActionName("Mark As Read")]
		public ActionResult MarkAsRead(Int32 id) {
			// Get the forum!
			var forum = this.GetRepository<Core.Forum>().Read(id);
			// Does the user have access to this forum?
			if (forum.HasAccess(AccessFlag.Read)) {
				// Let's iterate all topics!
				foreach (Topic topic in forum.Topics) {
					// Yes, so let's update the "last viewed" data!
					topic.Track();
				}
			}

			// Done, back to the forum view!
			return RedirectToAction("index", new { area = "forum", id = id, title = forum.Name.ToSlug() });
		}
		#endregion

		#region Follow/unfollow
		[Authorize]
		public ActionResult Follow(Int32 forumId) {
			Core.Forum forum = this.GetRepository<Core.Forum>().Read(forumId);

			// Any authenticated user?
			if (this.Authenticated) {
				FollowForum ff = new FollowForum(forum, this.ActiveUser);
				this.GetRepository<FollowForum>().Create(ff);
				this.Context.SaveChanges();
			}

			return RedirectToRoute("ShowForum", new RouteValueDictionary { { "id", forum.Id }, { "title", forum.Name.ToSlug() } });
		}

		[Authorize]
		public ActionResult UnFollow(Int32 forumId) {
			Core.Forum forum = this.GetRepository<Core.Forum>().Read(forumId);

			// Any authenticated user?
			if (this.Authenticated) {
				IRepository<FollowForum> ffRepository = this.GetRepository<FollowForum>();
				// Get the follow topic.
				FollowForum ff = ffRepository.ReadOne(new FollowForumSpecifications.SpecificForumAndUser(forum, this.ActiveUser));
				// Did we get one?
				if (ff != null) {
					// Then the user is following the topic, let's delete the "link".
					ffRepository.Delete(ff);

					this.Context.SaveChanges();
				}
			}

			return RedirectToRoute("ShowForum", new RouteValueDictionary { { "id", forum.Id }, { "title", forum.Name.ToSlug() } });
		}
		#endregion
	}
}